﻿#include "DiagramWidget.h"
#include <QTabWidget>
#include <QTimer>
#include <QDateTime>
#include "../src_dataProcessor/DataProcessor.h"

#include <QFileDialog>   //保存文件
#include <QMessageBox>
#include <QRegularExpression>

DiagramWidget::DiagramWidget(BasicData* data, ConfigUI* conf, QWidget* parent) : QWidget(parent){
	m_data = data;
	m_config = conf;
	m_alg = new Algorithoms(m_data, m_config);

	m_scaleFactor = 1.0;
	setMouseTracking(true);

	QTimer::singleShot(0, this, [this]() {initViewTransform(); });
	setMouseTracking(false);				// 关键设置：启用鼠标移动跟踪（无论是否按下）
	setAttribute(Qt::WA_TransparentForMouseEvents, false);// 同时确保不忽略未处理事件

	this->setContentsMargins(5, 5, 5, 5);  // 四周5px边距
	this->setAutoFillBackground(true);      // 必须启用
}
DiagramWidget::~DiagramWidget() {
	delete m_alg;
}

//外部触发的动作槽函数
void DiagramWidget::action_reset() {
	// 复位重置，文本地图则重新读取文件，图片地图则读取其对应的文本文件
	DataProcessor dp(m_data);
	m_data->clear();
	QString qFullName;
	if (m_data_type == 1) {			//图片地图
		QString txtName = QFileInfo(m_qFileName).completeBaseName() + ".txt";
		qFullName = QDir(m_qFilePath).filePath(txtName);
	}
	else if (m_data_type == 3) {	//文本地图
		qFullName = QDir(m_qFilePath).filePath(m_qFileName);
	}
	else {
		return;
	}
	dp.readFile(qFullName, 3);
	update();
}
void DiagramWidget::action_random_pin() {
	// 重新生成随机pin
	if (m_data_type != 1 && m_data_type != 3) return;	//文件类型不匹配
	const vector<QString>& symbols = m_data->m_mapSymbols;
	if (symbols.empty() || m_data->m_pinSum <= 0) return;
	int width = symbols[0].size();
	int height = symbols.size();
	m_data->m_flyLines = DataProcessor::getRandomPts(width, height, m_data->m_pinSum);
	m_data->m_wires.clear();
	m_data->m_wiresPts.clear();
	update();
}
void DiagramWidget::pin_decrease() {
	// pin数量减少
	if (m_data_type != 1 && m_data_type != 3) return;	//文件类型不匹配
	m_data->m_pinSum = (int)m_data->m_flyLines.size();
	if (m_data->m_pinSum <= 0) return;
	m_data->m_flyLines.pop_back();
	m_data->m_pinSum--;
	update();
}
void DiagramWidget::action_pin_increase() {
	// pin数量增加
	if (m_data_type != 1 && m_data_type != 3) return;	//文件类型不匹配
	const vector<QString>& symbols = m_data->m_mapSymbols;
	if (symbols.empty()) return;
	int width = symbols[0].size();
	int height = symbols.size();
	m_data->m_flyLines.emplace_back(DataProcessor::getRandomPts(width, height, 1).back());
	m_data->m_pinSum = (int)m_data->m_flyLines.size();
	update();
}
void DiagramWidget::action_save_case() {
	;
}
void DiagramWidget::action_save_image() {
	// 1. 准备文件对话框参数
	QString imageFilters = tr("PNG 文件 (*.png);;JPEG 文件 (*.jpg *.jpeg);;BMP 文件 (*.bmp);;WebP 文件 (*.webp)");
	QString defaultPath = m_qFilePath.isEmpty() ? "./data" : m_qFilePath;
	QString defaultFileName = m_qFileName.isEmpty() ? "untitled.png" : QFileInfo(m_qFileName).completeBaseName() + ".png";
	// 2. 打开文件对话框
	QString selectedFilter;
	QString fileName = QFileDialog::getSaveFileName(
		this,
		tr("保存图片"),
		defaultPath + "/" + defaultFileName, // 默认文件名
		imageFilters,
		&selectedFilter
	);
	if (fileName.isEmpty()) {
		qDebug() << "用户取消保存";
		return;
	}
	// 3. 自动补全扩展名
	QString expectedSuffix = selectedFilter.section('*', 1).section(')', 0, 0);
	if (!fileName.endsWith(expectedSuffix, Qt::CaseInsensitive)) {
		fileName += expectedSuffix;
	}
	// 4. 获取当前绘图内容
	QPixmap pixmap = grab(); // 捕获整个控件
	if (pixmap.isNull()) {
		QMessageBox::critical(this, tr("错误"), tr("无法获取绘图内容"));
		return;
	}
	// 5. 根据格式选择保存参数
	QString suffix = QFileInfo(fileName).suffix().toLower();
	bool saveSuccess = false;
	if (suffix == "png") {
		saveSuccess = pixmap.save(fileName, "PNG", 100); // 无损压缩
	}
	else if (suffix == "jpg" || suffix == "jpeg") {
		saveSuccess = pixmap.save(fileName, "JPEG", 95); // 高质量JPEG
	}
	else if (suffix == "bmp") {
		saveSuccess = pixmap.save(fileName, "BMP");
	}
	else if (suffix == "webp") {
		saveSuccess = pixmap.save(fileName, "WEBP", 90);
	}
	else {
		QMessageBox::warning(this, tr("警告"),
			tr("不支持的格式，已自动保存为PNG格式"));
		saveSuccess = pixmap.save(fileName + ".png", "PNG");
	}
	// 6. 处理保存结果
	if (saveSuccess) {
		m_config->m_qFilePath = QFileInfo(fileName).absolutePath(); // 记住路径
		qDebug() << "图像已保存至:" << fileName;
		QMessageBox::information(this, tr("成功"), tr("图像保存成功"));
	}
	else {
		qDebug() << "保存失败:" << fileName;
		QMessageBox::critical(this, tr("错误"),
			tr("保存失败，请检查文件路径和权限"));
	}
}
void DiagramWidget::action_write_pre() {
	//判断是否已经完成预布线
	if (m_data->m_wires.empty()) {
		QMessageBox::warning(this, tr("提示: "), tr("请先进行预布线!"));
		return;
	}
	//设置默认名称
	QString defaltName;
	QFileInfo fileInfo(m_qFullSaveName);  // 解析路径
	QString lastFileBaseName = fileInfo.baseName();  // 获取纯文件名（如 "123.json"）
	QRegularExpression regex("^\\d+$");
	QRegularExpressionMatch match = regex.match(lastFileBaseName);
	if (match.hasMatch()) {
		bool ok;
		int num = match.captured(0).toInt(&ok);
		if (ok) {
			defaltName = QString::number(num + 1) + ".json";
		}
	}
	//弹出保存文件对话框
	m_qFullSaveName = QFileDialog::getSaveFileName(this,
		tr("保存json文件"),
		m_qFilePath + "/" + defaltName,  // 拼接默认文件名
		tr("json files (*.json)")
	);

	//m_qFullSaveName = QFileDialog::getSaveFileName(this, tr("保存json文件"), m_qFilePath, tr("json files (*.json)"));
	if (m_qFullSaveName.isEmpty()) {
		qDebug() << "用户取消了保存操作";
		return;
	}
	DataProcessor::write_PPDT_Pre(m_qFullSaveName, m_data->m_flyLines, m_data->m_wiresPts, m_data->m_mapSymbols);
	m_resAdded = false;
}
void DiagramWidget::action_write_res() {
	if (!m_resAdded) {
		bool writeSucessed = DataProcessor::write_PPDT_Res(m_qFullSaveName, m_data->m_wiresPts, m_data->m_mapSymbols);
		if (writeSucessed) {
			m_resAdded = true;
			QMessageBox::information(this, tr("提示: "), tr("追加成功!"));
		}
		else {
			QMessageBox::warning(this, tr("警告: "), tr("追加失败!"));
		}
	}
	else {
		QMessageBox::warning(this, tr("警告: "), tr("禁止重复追加最终布线信息!"));
	}
	return;
}

void DiagramWidget::action_write_after_post() {
	QString qFullName = QFileDialog::getSaveFileName(this, tr("保存后处理之后的文件"), m_qFilePath, tr("文本文件 (*)"));
	DataProcessor::writeOutputData(qFullName, m_data->m_wires);
}

//########################## 绘图模块（私有） ##########################
//1.绘制模式
void DiagramWidget::paintVisibleContent(QPainter& painter, const QRect& rect) {
	//1.初始化
	painter.setRenderHint(QPainter::Antialiasing);
	painter.setPen(m_config->m_defaultPen);
	m_visibleRect = m_transform.inverted().mapRect(rect);		//获取可视区域（数据坐标系）
	//1.绘制背景
	if(m_config->m_showGrids)
		drawBackground(m_config->m_r, 1, painter);	//背景绘制模式，0不绘制网格，1高速绘制，2渲染绘制
	//2.绘制障碍物
	if (m_config->m_showObss) {
		//点状障碍物
		QPen obstaclePen(Qt::black, 2);		//黑色障碍物画笔
		painter.setPen(obstaclePen);
		drawObstacles(m_config->m_r, painter);	//多个多边形障碍物（多组）
		painter.setPen(m_config->m_defaultPen);

		//绘制多边形障碍物
		painter.setPen(m_config->m_blackPen);
		drawLines(m_data->m_polygonss, painter);
	}

	//3.绘制线段
	//绘制飞线
	if (m_config->m_showFlyLines) {
		QPen flyLinePen(QColor(255, 128, 128), 1);		//浅红色飞线画笔
		painter.setPen(flyLinePen);
		drawLines(m_data->m_flyLines, painter);		//飞线
	}
	//绘制多根走线
	if (m_config->m_showWires) {
		painter.setPen(m_config->m_defaultPen);
		drawLines(m_data->m_wires, painter);	//绘制线
	}
	//10.绘制多边形
	drawLines(m_data->m_polygon, painter);

	//4.绘制点
	painter.save();		// ⚡️ 保存当前状态，设置绘点的属性，之后恢复
	painter.setPen(Qt::NoPen);
	painter.setRenderHint(QPainter::Antialiasing);
	//绘制起点终点
	painter.setBrush(Qt::red);
	drawTwoPoints(m_data->m_flyLines, m_config->m_r, painter);
	//绘制规划点
	if (m_config->m_showCorners) {
		painter.setBrush(Qt::blue);
		drawPoints(m_data->m_cornerPts, m_config->m_r, painter);		//绘制规划点
	}
	//绘制路径上的点
	if (m_config->m_showPathPts) {
		painter.setBrush(Qt::magenta);	//浅品红
		drawPoints(m_data->m_wiresPts, m_config->m_r - 3, painter);		//路径点

		painter.setPen(m_config->m_defaultPen);
		drawLines(m_data->m_wiresPts, painter);			//连线
		painter.setPen(Qt::NoPen);
	}
	//绘制res点
	if (m_config->m_show_resPts) {
		painter.setBrush(Qt::green);
		drawPoints(m_data->m_resWiresPts, m_config->m_r - 3, painter);	//res点

		painter.setPen(m_config->m_resPen);
		drawLines(m_data->m_resWiresPts, painter);		//连线
		painter.setPen(Qt::NoPen);
	}

	painter.restore();  // ⚡️ 恢复所有保存的状态
}
void DiagramWidget::paintCacheContent(QPainter& painter, const QRect& rect) {
	// 1. 计算缓存绘制区域（考虑 m_cacheOffset）
	QRect sourceRect = rect.translated(m_cacheOffset);
	QPoint destPoint = rect.topLeft();

	// 2. 绘制缓存到屏幕（仅绘制可见部分）
	painter.drawPixmap(
		destPoint,          // 目标位置（屏幕坐标）
		m_canvasCache,		// 离屏缓存
		sourceRect          // 源区域（缓存坐标系）
	);
}
//2.绘制各个模块
void DiagramWidget::drawBackground(int r, int paintNetType, QPainter& painter) {
	//1.根据地图大小，强制修改绘制方案
	if (m_data->m_net.size() < 2 || m_data->m_net[0].empty() || m_data->m_net[1].empty()) return;
	painter.save(); // 保存当前状态
	painter.setRenderHint(QPainter::Antialiasing, false);	// 禁用抗锯齿
	painter.setPen(QPen(Qt::black, 2)); // 1像素浅灰色实线
	//绘制边框
	drawLines(m_data->m_net[0][0], painter);
	drawLines(m_data->m_net[0].back(), painter);
	drawLines(m_data->m_net[1][0], painter);
	drawLines(m_data->m_net[1].back(), painter);
	painter.restore(); // 恢复状态
	int steps = 1;
	if (m_scaleFactor < 1.1) {
		steps = 1;
		r = 1;
	}
	else if (m_scaleFactor < 2.5) {
		steps = 2;
		if (m_scaleFactor < 0.6)
			steps = m_scaleFactor < 0.3 ? 8 : 4;
	}
	//2.绘制网格
	if (paintNetType == 0) {									//1.1不绘制网格
		;
	}
	else if (paintNetType == 1) {								//1.2绘制高性能网格(不渲染))
		painter.save(); // 保存当前状态
		painter.setRenderHint(QPainter::Antialiasing, false);	// 禁用抗锯齿
		painter.setPen(QPen(Qt::lightGray, 1)); // 1像素浅灰色实线
		// 仅绘制可见区域内的网格线
		// 预计算转换矩阵（避免每次调用dataToScreen）
		const QTransform& transform = m_transform;
		// 遍历网格线
		auto& lines = m_data->m_net;
		int len1 = (int)lines[0].size() - 1;
		int len2 = (int)lines[1].size() - 1;
		QPointF p1, p2;
		for (int i = 1; i < len1; i += steps) {		//绘制竖线（x逐条递增）
			// 1. 裁剪检测
			p1 = lines[0][i].p1();	//定义在循环外，减少内存分配
			p2 = lines[0][i].p2();
			if (!clipLine(p1, p2, m_visibleRect)) continue;
			// 2. 坐标转换 + 绘制（合并操作）
			painter.drawLine(
				transform.map(p1),  // 等效于dataToScreen(p1)
				transform.map(p2)   // 等效于dataToScreen(p2)
			);
		}
		for (int i = 1; i < len2; i += steps) {		//绘制横线线（y逐条递增）
			// 1. 裁剪检测
			p1 = lines[1][i].p1();	//定义在循环外，减少内存分配
			p2 = lines[1][i].p2();
			if (!clipLine(p1, p2, m_visibleRect)) continue;
			// 2. 坐标转换 + 绘制（合并操作）
			painter.drawLine(
				transform.map(p1),  // 等效于dataToScreen(p1)
				transform.map(p2)   // 等效于dataToScreen(p2)
			);
		}
		painter.restore(); // 恢复状态
	}
	else if (paintNetType == 2) {								//1.3绘制渲染网格
		drawLines(m_data->m_net, painter);
	}
}
void DiagramWidget::drawObstacles(int r, QPainter& painter) {
	if (r > 1) {
		painter.save();		// ⚡️ 保存当前状态，设置绘点的属性，之后恢复
		painter.setPen(Qt::NoPen);
		painter.setRenderHint(QPainter::Antialiasing);
		painter.setBrush(Qt::black);
		drawPoints(m_data->m_Pts, r, painter);
		painter.restore();  // ⚡️ 恢复所有保存的状态
	}
	else {
		for (auto& point : m_data->m_Pts) {
			painter.drawPoint(dataToScreen(point));
		}
	}
}

//3.绘制点
void DiagramWidget::drawPoints(QPointF point, int r, QPainter& painter) {
	QPointF pt = point;
	//1.裁剪数据（点，直接过滤）
	if (clipPt(pt, m_visibleRect)) {
		//2,3.转换为屏幕坐标，绘制
		painter.drawEllipse(dataToScreen(pt), r, r);
	}
}
void DiagramWidget::drawPoints(LineUI line, int r, QPainter& painter) {
	QPointF pt1 = line.p1();
	QPointF pt2 = line.p2();
	//1.裁剪数据（点，直接过滤）
	if (clipPt(pt1, m_visibleRect)) {
		//2,3.转换为屏幕坐标，绘制
		painter.drawEllipse(dataToScreen(pt1), r, r);
	}
	if (clipPt(pt2, m_visibleRect)) {
		//2,3.转换为屏幕坐标，绘制
		painter.drawEllipse(dataToScreen(pt2), r, r);
	}
}
void DiagramWidget::drawPoints(vector<QPointF>& points, int r, QPainter& painter) {
	QPointF pt;
	for (auto& point : points) {
		pt = point;
		//1.裁剪数据（点，直接过滤）
		if (clipPt(pt, m_visibleRect)) {
			//2,3.转换为屏幕坐标，绘制
			painter.drawEllipse(dataToScreen(pt), r, r);
		}
	}
}
void DiagramWidget::drawPoints(vector<vector<QPointF>>& points, int r, QPainter& painter) {
	for (auto& point : points) {
		drawPoints(point, r, painter);
	}
}
void DiagramWidget::drawPoints(vector<LineUI>& lines, int r, QPainter& painter) {
	if (lines.empty()) return;
	QPointF pt = lines[0].p1();
	//1.裁剪数据（点，直接过滤）
	if (clipPt(pt, m_visibleRect)) {
		//2,3.转换为屏幕坐标，绘制
		painter.drawEllipse(dataToScreen(pt), r, r);
	}
	for (auto& line : lines) {
		pt = line.p2();
		//1.裁剪数据（点，直接过滤）
		if (clipPt(pt, m_visibleRect)) {
			//2,3.转换为屏幕坐标，绘制
			painter.drawEllipse(dataToScreen(pt), r, r);
		}
	}
}
void DiagramWidget::drawPoints(vector<vector<LineUI>>& lines, int r, QPainter& painter) {
	for (auto& line : lines) {
		drawPoints(line, r, painter);
	}
}
void DiagramWidget::drawTwoPoints(vector<LineUI>& lines, int r, QPainter& painter) {
	for (auto& line : lines) {
		drawPoints(line, r, painter);
	}
}
void DiagramWidget::drawTwoPoints(vector<vector<LineUI>>& lines, int r, QPainter& painter) {
	for (auto& line : lines) {
		drawTwoPoints(line, r, painter);
	}
}
//4.绘制线段
void DiagramWidget::drawLines(LineUI& line, QPainter& painter) {
	//1.裁剪数据（线段，修改端点，保留区域内部分）
	LineUI line_to_paint = line;
	if (!clipLine(line_to_paint, m_visibleRect)) return;
	//2.转换为屏幕坐标
	QPointF screenP1 = dataToScreen(line_to_paint.p1());
	QPointF screenP2 = dataToScreen(line_to_paint.p2());
	//3.绘制线段
	painter.drawLine(screenP1, screenP2);
}
void DiagramWidget::drawLines(vector<LineUI>& lines, QPainter& painter) {
	for (auto& line : lines) {
		drawLines(line, painter);
	}
}
void DiagramWidget::drawLines(vector<vector<LineUI>>& lines, QPainter& painter) {
	for (auto& line : lines) {
		drawLines(line, painter);
	}
}
void DiagramWidget::drawLines(vector<vector<vector<LineUI>>>& lines, QPainter& painter) {
	for (auto& line : lines) {
		drawLines(line, painter);
	}
}
void DiagramWidget::drawLines(vector<vector<vector<vector<LineUI>>>>& lines, QPainter& painter) {
	for (auto& line : lines) {
		drawLines(line, painter);
	}
}
void DiagramWidget::drawLines(vector<QPointF>& points, QPainter& painter) {
	int points_sum = points.size();
	if (points_sum <= 1) return;
	LineUI line_to_paint;
	for (int i = 0; i < points_sum - 1; ++i) {
		line_to_paint = LineUI(points[i], points[i + 1]);
		drawLines(line_to_paint, painter);
	}
}
void DiagramWidget::drawLines(vector<vector<QPointF>>& points, QPainter& painter) {
	for (auto& point : points) {
		drawLines(point, painter);
	}
}

//5.辅助函数
//5.1数据坐标转屏幕坐标
QPointF DiagramWidget::dataToScreen(const QPointF& dataPoint) const {
	return m_transform.map(dataPoint);
}
LineUI DiagramWidget::dataToScreen(const LineUI& dataLine) const {
	return m_transform.map(dataLine);
}
QPointF DiagramWidget::screenToData(const QPointF& screenPoint) const {
	QPointF dataPoint = m_transform.inverted().map(screenPoint);
	return { dataPoint.x(), dataPoint.y() };
}
void DiagramWidget::updateTransform() {
	m_transform = QTransform()	// 变换矩阵，作用：原始数据-->绘制的数据	
		.translate(m_panOffset.x(), m_panOffset.y())	//平移变换
		.scale(m_scaleFactor, m_scaleFactor);			//缩放变换
}

//5.2线段裁剪
bool DiagramWidget::clipLine(QPointF& p1, QPointF& p2, const QRectF& viewport) const {
	// 使用Liang-Barsky算法裁剪
	qreal x0 = p1.x(), y0 = p1.y();
	qreal x1 = p2.x(), y1 = p2.y();

	qreal t0 = 0.0, t1 = 1.0;
	qreal dx = x1 - x0, dy = y1 - y0;

	// 遍历所有裁剪边界
	for (int edge = 0; edge < 4; ++edge) {
		qreal p = 0, q = 0;

		switch (edge) {
		case 0: p = -dx; q = x0 - viewport.left(); break; // left
		case 1: p = dx; q = viewport.right() - x0; break; // right
		case 2: p = -dy; q = y0 - viewport.top(); break; // top
		case 3: p = dy; q = viewport.bottom() - y0; break; // bottom
		}

		if (p == 0) {
			if (q < 0) return false; // 线段平行且在裁剪区域外
		}
		else {
			qreal r = q / p;
			if (p < 0) {
				if (r > t1) return false;
				if (r > t0) t0 = r;
			}
			else {
				if (r < t0) return false;
				if (r < t1) t1 = r;
			}
		}
	}
	p1.setX(x0 + t0 * dx);
	p1.setY(y0 + t0 * dy);
	p2.setX(x0 + t1 * dx);
	p2.setY(y0 + t1 * dy);
	return true;
}
bool DiagramWidget::clipLine(LineUI& line, const QRectF& viewport) const {
	// 使用Liang-Barsky算法裁剪
	qreal x0 = line.p1().x(), y0 = line.p1().y();
	qreal x1 = line.p2().x(), y1 = line.p2().y();

	qreal t0 = 0.0, t1 = 1.0;
	qreal dx = x1 - x0, dy = y1 - y0;

	// 遍历所有裁剪边界
	for (int edge = 0; edge < 4; ++edge) {
		qreal p = 0, q = 0;

		switch (edge) {
		case 0: p = -dx; q = x0 - viewport.left(); break; // left
		case 1: p = dx; q = viewport.right() - x0; break; // right
		case 2: p = -dy; q = y0 - viewport.top(); break; // top
		case 3: p = dy; q = viewport.bottom() - y0; break; // bottom
		}

		if (p == 0) {
			if (q < 0) return false; // 线段平行且在裁剪区域外
		}
		else {
			qreal r = q / p;
			if (p < 0) {
				if (r > t1) return false;
				if (r > t0) t0 = r;
			}
			else {
				if (r < t0) return false;
				if (r < t1) t1 = r;
			}
		}
	}
	line.setLine(x0 + t0 * dx, y0 + t0 * dy, x0 + t1 * dx, y0 + t1 * dy);
	return true;
}
bool DiagramWidget::clipPt(QPointF& p1, const QRectF& viewport) const {
	if (p1.x() < viewport.left() || p1.x() > viewport.right() ||
		p1.y() < viewport.top() || p1.y() > viewport.bottom()) {
		return false;
	}
	return true;
}

//5.3视图初始化
void DiagramWidget::initViewTransform() {
	QSize effectiveSize;
	if (QTabWidget* tabWidget = qobject_cast<QTabWidget*>(parentWidget())) {	// 通过父容器获取
		effectiveSize = tabWidget->currentWidget()->size();
	}
	else {
		effectiveSize = size();		//直接获取可用尺寸
	}
	if (effectiveSize.width() <= 10 || effectiveSize.height() <= 10) {
		effectiveSize = QSize(800, 600); // 默认尺寸
	}
	// 计算数据边界
	QRectF dataBounds(m_data->minPt, m_data->maxPt);
	// 计算初始缩放比例（保留5%边距）
	qreal xScale = (effectiveSize.width() * 0.95) / dataBounds.width();
	qreal yScale = (effectiveSize.height() * 0.95) / dataBounds.height();
	m_scaleFactor = qMin(xScale, yScale);
	// 计算初始平移（居中）,即数据被缩放到相应大小，并且移动到最左侧之后，然后进行m_panOffset平移即可居中
	m_panOffset = QPointF(
		(effectiveSize.width() - dataBounds.width() * m_scaleFactor) / 2 - m_data->minPt.x() * m_scaleFactor,
		(effectiveSize.height() - dataBounds.height() * m_scaleFactor) / 2 - m_data->minPt.y() * m_scaleFactor
	);
	updateTransform();
}


//########################## 事件管理（保护） ##########################
//1.综合事件：【初始化显示】，【resize】，【定时刷新】，【绘图事件】
void DiagramWidget::showEvent(QShowEvent* event) {
	//当控件首次显示或从隐藏状态变为可见状态时，自动调用该函数，一般用于执行初始化操作
	QWidget::showEvent(event);
	QTimer::singleShot(100, this, [this]() { // 延迟确保布局完成
		initViewTransform();
		});
}
void DiagramWidget::resizeEvent(QResizeEvent* event) {
	QWidget::resizeEvent(event);
	update();  // 触发paintEvent
	// 或者需要重新计算变换时：
	initViewTransform();
	update();
}
void DiagramWidget::timerEvent(QTimerEvent* event) {
	if (event->timerId() == m_moveTimer.timerId()) {
		m_moveTimer.stop(); // 单次触发
		moveRun();
	}
}
void DiagramWidget::paintEvent(QPaintEvent* event) {
	QPainter painter(this);
	m_config->m_r = m_scaleFactor >= 5 ? m_scaleFactor / 3 : 2;	//绘制实心点的半径
	//2.绘图
	if (m_cacheValid) {		//利用缓存快速绘制
		paintCacheContent(painter, event->rect());
	}
	else {					// 正常绘制流程
		paintVisibleContent(painter, event->rect());
	}
}

//2.鼠标事件
void DiagramWidget::mousePressEvent(QMouseEvent* event) {
	if (event->button() == Qt::LeftButton) {
		m_config->painState = PUSH_LINE;		//推线模式（此变量决定timerEvent刷新什么函数）
	}
	else if (event->button() == Qt::MiddleButton) {
		m_config->painState = MOVE_CANVAS;	//移动画布
	}
	m_pressPos = event->pos();
	pressRun(m_pressPos);
}
void DiagramWidget::mouseMoveEvent(QMouseEvent* event) {
	if (!m_moveTimer.isActive()) {
		m_moveTimer.start(20, this);
	}
	m_movePos = event->pos();
}
void DiagramWidget::mouseReleaseEvent(QMouseEvent* event) {
	m_releasePos = event->pos();
	releaseRun(m_releasePos);
	if (event->button() == Qt::LeftButton) {
		m_config->painState = NONE;
	}
	else if (event->button() == Qt::MiddleButton) {
		m_config->painState = NONE;
	}
}
void DiagramWidget::wheelEvent(QWheelEvent* event) {
	if (m_config->painState == NONE) {
		int delta = event->angleDelta().y();		//鼠标滚动的度数
		QPointF mouseScenePos = event->position();	//浮点坐标
		wheelEventRun(delta, mouseScenePos);
		event->accept(); // 阻止事件继续传播
	}
}

//3.鼠标事件执行逻辑
//左键按下时，设置需要编辑的对象
void DiagramWidget::pressRun(const QPoint& pressPos) {
	QPointF clickPt = screenToData(pressPos);
	if (m_config->painState == PUSH_LINE) {
		setPushLines(clickPt);
		if (!(m_slLine.lineSelected || m_slLine.pinSelected)) {		//未选中线,则将命令切换为移动画布
			m_config->painState = MOVE_CANVAS;
			setCursor(Qt::ClosedHandCursor); // 改变光标形状
			moveCanvasBegin(clickPt);		//生成缓存区
		}
	}
	else if (m_config->painState == MOVE_CANVAS) {
		setCursor(Qt::ClosedHandCursor); // 改变光标形状
		moveCanvasBegin(clickPt);		//生成缓存区
	}
}
//鼠标移动时，需要在timerEvent中周期执行的内容
void DiagramWidget::moveRun() {
	if (m_config->painState == PUSH_LINE) {
		if (m_slLine.lineSelected || m_slLine.pinSelected) {	// 选中线段时，执行推线操作
			pushLineActivate();
		}
	}
	else if (m_config->painState == MOVE_CANVAS) {
		moveCanvasActivate();		//持续更新，绘制移动画布
	}
}
//鼠标释放时，执行一次的内容
void DiagramWidget::releaseRun(const QPoint& releasePos) {
	if (m_config->painState == PUSH_LINE) {
		if (m_slLine.lineSelected || m_slLine.pinSelected) {
			pushLineEnd(screenToData(releasePos));
		}
	}
	else if (m_config->painState == MOVE_CANVAS) {
		setCursor(Qt::ArrowCursor); // 恢复光标形状
		moveCanvasEnd(releasePos);
	}
}
//滚动滚轮时，执行的内容
void DiagramWidget::wheelEventRun(const int& delta, const QPointF& screenPos) {
	// 1. 获取鼠标在数据坐标系中的位置
	QPointF mouseDataPos = m_transform.inverted().map(screenPos);

	// 2. 计算缩放因子（非线性缩放更平滑）
	const qreal zoomFactor = 1.1; // 每次滚轮步进的缩放系数
	qreal scaleFactor = (delta > 0) ? zoomFactor : 1.0 / zoomFactor;

	// 3. 限制缩放范围
	qreal newScale = m_scaleFactor * scaleFactor;
	newScale = qBound(0.0001, newScale, 10000.0); // 限制在0.001~1000倍之间

	// 4. 计算保持鼠标点不变的平移量
	QPointF mousePosBefore = m_transform.map(mouseDataPos);
	m_scaleFactor = newScale;
	updateTransform(); // 先更新变换矩阵
	QPointF mousePosAfter = m_transform.map(mouseDataPos);
	m_panOffset += mousePosBefore - mousePosAfter;

	// 5. 应用新变换
	updateTransform();
	update();
}


//########################## 事件执行（私有） ##########################
//具体实现
//鼠标按下时，执行一次的内容集合
void DiagramWidget::setPushLines(const QPointF& clickPt) {
	//根据鼠标点击点，设置推挤的走线和推挤的线段相关信息，郭所所点位置没有走线，则返回false
	double snapThresholdPixels = 6.0; // 固定6像素容差
	double snapThreshold = snapThresholdPixels / m_scaleFactor;
	//将m_wires设置为推挤线
	m_slLine.isPushPin = setSelectedPt(clickPt, m_data->m_flyLines, snapThreshold);
	if (!m_slLine.pinSelected) {
		setSelectedPt(clickPt, m_data->m_wires, snapThreshold);
	}
	if (!m_slLine.pinSelected) {
		setSelectedLine(clickPt, m_data->m_wires, snapThreshold);
	}
	if (m_slLine.pinSelected) {
		m_alg->pushPinRunBegin(m_slLine.nearistPt, *m_slLine.linesToRun);
	}
	else if (m_slLine.lineSelected) {		//选中线段时，执行推线操作
		m_alg->pushLineRunBegin(m_slLine.sLine, *m_slLine.linesToRun);
	}
}
void DiagramWidget::moveCanvasBegin(const QPointF& clickPt) {
	// 计算缓存区域
	QRect cacheRect = rect();
	//QSize cacheSize = cacheRect.size() * 2; //（扩展2倍可见区域，鼠标拖移时，画布仍然可见）
	//cacheRect = QRect(cacheRect.topLeft() + m_pressPos - cacheRect.bottomRight(), cacheSize);
	// 生成缓存图像
	m_canvasCache = QPixmap(cacheRect.size());		//创建离屏缓存
	m_canvasCache.fill(Qt::transparent);	//填充为透明色
	QPainter cachePainter(&m_canvasCache);	//以缓存图像为画布，创建画家对象
	// 在缓存图像上绘制内容
	paintVisibleContent(cachePainter, cacheRect); // 调用你的绘制逻辑
	m_cacheValid = true;
}

//鼠标移动时，需持续执行的内容集合
void DiagramWidget::pushLineActivate() {
	//持续重新画图的核心逻辑
	QPointF currentPtPos = screenToData(m_movePos);
	m_slLine.offset = currentPtPos - m_slLine.nearistPt;
	if (m_slLine.pinSelected) {			//执行推点算法
		m_alg->pushPinRun(m_slLine.offset, false);
	}
	else if (m_slLine.lineSelected) {	//执行推线算法
		m_alg->pushLineRun(m_slLine.offset, false, m_config->m_postOn);
	}
	update();
}
void DiagramWidget::moveCanvasActivate() {
	if (!m_cacheValid) return;
	m_cacheOffset = m_pressPos - m_movePos;
	update(); // 触发重绘（此时 paintEvent 会直接绘制缓存）
}
//鼠标释放时，执行一次的内容集合
void DiagramWidget::pushLineEnd(const QPointF& releasePt) {
	m_slLine.offset = releasePt - m_slLine.nearistPt;
	if (m_slLine.pinSelected) {			//执行推点算法
		m_alg->pushPinRunOver(m_slLine.offset);
	}
	else if (m_slLine.lineSelected) {	//执行推线算法
		m_alg->pushLineRunOver(m_slLine.offset);
	}


	if (!m_slLine.isPushPin) {		//推线或者推拐点(不是推Pin)
		vector<vector<LineUI>>& wires = m_data->m_wires;
		DataProcessor::setPtsFromLines(wires, m_data->m_wiresPts);		//更新路径上的点
		for (int i = 0; i < wires.size(); ++i) {	//更新起点终点信息（推线可能改变了起点终点位置）
			if (wires[i].empty()) continue;
			if (m_data->m_flyLines.empty())  continue;
			m_data->m_flyLines[i].setP1(wires[i][0].p1());
			m_data->m_flyLines[i].setP2(wires[i].back().p2());
		}
	}

	m_slLine.clear();
	update();
}
void DiagramWidget::moveCanvasEnd(const QPoint& releasePos) {
	// 计算实际偏移量（数据坐标系）
	m_panOffset += releasePos - m_pressPos;
	updateTransform();// 更新实际变换
	m_cacheValid = false;
	update();
	m_canvasCache = QPixmap(); // 清除缓存
}


// 吸附阈值（数据坐标系）
bool DiagramWidget::setSelectedPt(const QPointF& clickPt, const LineUI& line, double snapThreshold) {
	qreal pickPointTolerance = m_config->m_r / m_scaleFactor;		//转化为数据坐标系（点的半径）
	pickPointTolerance *= 0.6;									// 缩小吸附范围，点视觉半径的0.8倍
	if (LineUI(clickPt, line.p1()).length() < pickPointTolerance) {
		m_slLine.pinSelected = true;
		m_slLine.nearistPt = line.p1();
		return true;
	}
	else if (LineUI(clickPt, line.p2()).length() < pickPointTolerance) {
		m_slLine.pinSelected = true;
		m_slLine.nearistPt = line.p2();
		return true;
	}
	return false;
}
bool DiagramWidget::setSelectedPt(const QPointF& clickPt, vector<LineUI>& lines, double snapThreshold) {
	for (auto& line : lines) {
		setSelectedPt(clickPt, line, snapThreshold);
		if (m_slLine.pinSelected) {
			m_slLine.linesToRun = &lines;
			return true;
		}
	}
	return false;
}
bool DiagramWidget::setSelectedPt(const QPointF& clickPt, vector<vector<LineUI>>& lines, double snapThreshold) {
	for (auto& line : lines) {
		setSelectedPt(clickPt, line, snapThreshold);
		if (m_slLine.pinSelected) {
			return true;
		}
	}
	return false;
}
bool DiagramWidget::setSelectedPt(const QPointF& clickPt, vector<vector<vector<LineUI>>>& lines, double snapThreshold) {
	for (auto& line : lines) {
		setSelectedPt(clickPt, line, snapThreshold);
		if (m_slLine.pinSelected) {
			return true;
		}
	}
	return false;
}

bool DiagramWidget::setSelectedLine(const QPointF& clickPt, const LineUI& line, double snapThreshold) {
	const QPointF p1 = line.p1();
	const QPointF p2 = line.p2();
	// 处理零长度线段
	if (p1 == p2) {
		if (sqrt(pow(clickPt.x() - p1.x(), 2) + pow(clickPt.y() - p1.y(), 2)) < const_minValue);
		return false;
	}
	// 向量计算
	const QPointF AP = clickPt - p1;
	const QPointF AB = p2 - p1;
	const double dotProduct = AP.x() * AB.x() + AP.y() * AB.y();
	const double abLengthSquared = AB.x() * AB.x() + AB.y() * AB.y();
	const double ratio = qBound(0.0, dotProduct / abLengthSquared, 1.0);
	// 计算最近点
	const QPointF nearestPoint = p1 + ratio * AB;
	const double distance = sqrt(pow(clickPt.x() - nearestPoint.x(), 2) + pow(clickPt.y() - nearestPoint.y(), 2));
	//自动吸附线逻辑
	qreal pinMinDist = m_config->m_r / m_scaleFactor;
	if (distance < snapThreshold) {
		m_slLine.lineSelected = true;
		m_slLine.sLine = line;
		m_slLine.nearistPt = nearestPoint;
		return true;
	}
	return false;
}
bool DiagramWidget::setSelectedLine(const QPointF& clickPt, vector<LineUI>& lines, double snapThreshold) {
	for (auto& line : lines) {
		setSelectedLine(clickPt, line, snapThreshold);
		if (m_slLine.lineSelected) {
			m_slLine.linesToRun = &lines;
			return true;
		}
	}
	return false;
}
bool DiagramWidget::setSelectedLine(const QPointF& clickPt, vector<vector<LineUI>>& lines, double snapThreshold) {
	for (auto& line : lines) {
		setSelectedLine(clickPt, line, snapThreshold);
		if (m_slLine.lineSelected) {
			return true;
		}
	}
	return false;
}
bool DiagramWidget::setSelectedLine(const QPointF& clickPt, vector<vector<vector<LineUI>>>& lines, double snapThreshold) {
	for (auto& line : lines) {
		setSelectedLine(clickPt, line, snapThreshold);
		if (m_slLine.lineSelected) {
			return true;
		}
	}
	return false;
}


//与算法相关的槽函数
void DiagramWidget::setPPDTRun() {
	// 如果未打开文件,即m_data为空指针，则弹窗提示并返回
	if (this == nullptr || m_data == nullptr) {
		QMessageBox::warning(this, "提示", "请先打开文件！");
		return;
	}
	m_alg->PPDTRunBegin();
	m_alg->PPDTRun();
	DataProcessor::setPtsFromLines(m_data->m_wires, m_data->m_wiresPts);
	update();
}

void DiagramWidget::setPolyRun() {
	if (this == nullptr || m_data == nullptr) {
		QMessageBox::warning(this, "提示", "请先打开文件！");
		return;
	}
	m_alg->polyRunBegin();
	m_alg->polyRun();
	DataProcessor::setPtsFromLines(m_data->m_wires, m_data->m_wiresPts);
	update();
}
